﻿namespace System.ServiceModel.DomainServices.Client.ApplicationServices
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security.Principal;
    using System.ServiceModel.DomainServices.Client;
    using System.Threading;

    public abstract class AuthenticationService : INotifyPropertyChanged
    {
        private readonly SynchronizationContext _synchronizationContext = (SynchronizationContext.Current ?? new SynchronizationContext());
        private readonly object _syncLock = new object();
        private IPrincipal _user;
        private EventHandler<AuthenticationEventArgs> loggedIn;
        private EventHandler<AuthenticationEventArgs> loggedOut;
        private PropertyChangedEventHandler propertyChangedEventHandler;

        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add { throw new NotImplementedException(); }
            remove { throw new NotImplementedException(); }
        }

        public event EventHandler<AuthenticationEventArgs> LoggedIn
        {
            add
            {
                EventHandler<AuthenticationEventArgs> handler2;
                EventHandler<AuthenticationEventArgs> loggedIn = this.loggedIn;
                do
                {
                    handler2 = loggedIn;
                    EventHandler<AuthenticationEventArgs> handler3 = (EventHandler<AuthenticationEventArgs>) Delegate.Combine(handler2, value);
                    loggedIn = Interlocked.CompareExchange<EventHandler<AuthenticationEventArgs>>(ref this.loggedIn, handler3, handler2);
                }
                while (loggedIn != handler2);
            }
            remove
            {
                EventHandler<AuthenticationEventArgs> handler2;
                EventHandler<AuthenticationEventArgs> loggedIn = this.loggedIn;
                do
                {
                    handler2 = loggedIn;
                    EventHandler<AuthenticationEventArgs> handler3 = (EventHandler<AuthenticationEventArgs>) Delegate.Remove(handler2, value);
                    loggedIn = Interlocked.CompareExchange<EventHandler<AuthenticationEventArgs>>(ref this.loggedIn, handler3, handler2);
                }
                while (loggedIn != handler2);
            }
        }

        public event EventHandler<AuthenticationEventArgs> LoggedOut
        {
            add
            {
                EventHandler<AuthenticationEventArgs> handler2;
                EventHandler<AuthenticationEventArgs> loggedOut = this.loggedOut;
                do
                {
                    handler2 = loggedOut;
                    EventHandler<AuthenticationEventArgs> handler3 = (EventHandler<AuthenticationEventArgs>) Delegate.Combine(handler2, value);
                    loggedOut = Interlocked.CompareExchange<EventHandler<AuthenticationEventArgs>>(ref this.loggedOut, handler3, handler2);
                }
                while (loggedOut != handler2);
            }
            remove
            {
                EventHandler<AuthenticationEventArgs> handler2;
                EventHandler<AuthenticationEventArgs> loggedOut = this.loggedOut;
                do
                {
                    handler2 = loggedOut;
                    EventHandler<AuthenticationEventArgs> handler3 = (EventHandler<AuthenticationEventArgs>) Delegate.Remove(handler2, value);
                    loggedOut = Interlocked.CompareExchange<EventHandler<AuthenticationEventArgs>>(ref this.loggedOut, handler3, handler2);
                }
                while (loggedOut != handler2);
            }
        }

        event PropertyChangedEventHandler PropertyChanged;

        protected AuthenticationService()
        {
        }

        protected internal abstract IAsyncResult BeginLoadUser(AsyncCallback callback, object state);
        protected internal abstract IAsyncResult BeginLogin(LoginParameters parameters, AsyncCallback callback, object state);
        protected internal abstract IAsyncResult BeginLogout(AsyncCallback callback, object state);
        protected internal abstract IAsyncResult BeginSaveUser(IPrincipal user, AsyncCallback callback, object state);
        protected internal virtual void CancelLoadUser(IAsyncResult asyncResult)
        {
            if (!this.SupportsCancellation)
            {
                throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, Resources.ApplicationServices_OperationCannotCancel, new object[] { "LoadUser" }));
            }
        }

        protected internal virtual void CancelLogin(IAsyncResult asyncResult)
        {
            if (!this.SupportsCancellation)
            {
                throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, Resources.ApplicationServices_OperationCannotCancel, new object[] { "Login" }));
            }
        }

        protected internal virtual void CancelLogout(IAsyncResult asyncResult)
        {
            if (!this.SupportsCancellation)
            {
                throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, Resources.ApplicationServices_OperationCannotCancel, new object[] { "Logout" }));
            }
        }

        protected internal virtual void CancelSaveUser(IAsyncResult asyncResult)
        {
            if (!this.SupportsCancellation)
            {
                throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, Resources.ApplicationServices_OperationCannotCancel, new object[] { "SaveUser" }));
            }
        }

        protected abstract IPrincipal CreateDefaultUser();
        protected internal abstract LoadUserResult EndLoadUser(IAsyncResult asyncResult);
        protected internal abstract LoginResult EndLogin(IAsyncResult asyncResult);
        protected internal abstract LogoutResult EndLogout(IAsyncResult asyncResult);
        protected internal abstract SaveUserResult EndSaveUser(IAsyncResult asyncResult);
        private static string GetBusyPropertyName(AuthenticationOperation operation)
        {
            Type type = operation.GetType();
            if (typeof(LoginOperation) == type)
            {
                return "IsLoggingIn";
            }
            if (typeof(LogoutOperation) == type)
            {
                return "IsLoggingOut";
            }
            if (typeof(LoadUserOperation) == type)
            {
                return "IsLoadingUser";
            }
            if (typeof(SaveUserOperation) == type)
            {
                return "IsSavingUser";
            }
            return string.Empty;
        }

        private static void HandleOperationError(AuthenticationOperation ao)
        {
            if (ao.HasError)
            {
                ao.MarkErrorAsHandled();
            }
        }

        public LoadUserOperation LoadUser()
        {
            return this.LoadUser(null, null);
        }

        public LoadUserOperation LoadUser(Action<LoadUserOperation> completeAction, object userState)
        {
            this.StartOperation(new LoadUserOperation(this, this.WrapCompleteAction<LoadUserOperation>(completeAction), userState));
            return (LoadUserOperation) this.Operation;
        }

        public LoginOperation Login(LoginParameters parameters)
        {
            return this.Login(parameters, null, null);
        }

        public LoginOperation Login(string userName, string password)
        {
            return this.Login(new LoginParameters(userName, password));
        }

        public LoginOperation Login(LoginParameters parameters, Action<LoginOperation> completeAction, object userState)
        {
            this.StartOperation(new LoginOperation(this, parameters, this.WrapCompleteAction<LoginOperation>(completeAction), userState));
            return (LoginOperation) this.Operation;
        }

        public LogoutOperation Logout(bool throwOnError)
        {
            Action<LogoutOperation> completeAction = !throwOnError ? new Action<LogoutOperation>(AuthenticationService.HandleOperationError) : null;
            return this.Logout(completeAction, null);
        }

        public LogoutOperation Logout(Action<LogoutOperation> completeAction, object userState)
        {
            this.StartOperation(new LogoutOperation(this, this.WrapCompleteAction<LogoutOperation>(completeAction), userState));
            return (LogoutOperation) this.Operation;
        }

        private void OnLoggedIn(AuthenticationEventArgs e)
        {
            SendOrPostCallback callback = null;
            EventHandler<AuthenticationEventArgs> handler = this.loggedIn;
            if (handler != null)
            {
                if (callback == null)
                {
                    callback = delegate (object state) {
                        handler(this, e);
                    };
                }
                this.RunInSynchronizationContext(callback, null);
            }
        }

        private void OnLoggedOut(AuthenticationEventArgs e)
        {
            SendOrPostCallback callback = null;
            EventHandler<AuthenticationEventArgs> handler = this.loggedOut;
            if (handler != null)
            {
                if (callback == null)
                {
                    callback = delegate (object state) {
                        handler(this, e);
                    };
                }
                this.RunInSynchronizationContext(callback, null);
            }
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            SendOrPostCallback callback = null;
            PropertyChangedEventHandler handler = this.propertyChangedEventHandler;
            if (handler != null)
            {
                if (callback == null)
                {
                    callback = delegate (object state) {
                        handler(this, e);
                    };
                }
                this.RunInSynchronizationContext(callback, null);
            }
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }
            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        private void RunInSynchronizationContext(SendOrPostCallback callback, object state)
        {
            if (SynchronizationContext.Current == this._synchronizationContext)
            {
                callback(state);
            }
            else
            {
                this._synchronizationContext.Post(callback, state);
            }
        }

        public SaveUserOperation SaveUser(bool throwOnError)
        {
            Action<SaveUserOperation> completeAction = !throwOnError ? new Action<SaveUserOperation>(AuthenticationService.HandleOperationError) : null;
            return this.SaveUser(completeAction, null);
        }

        public SaveUserOperation SaveUser(Action<SaveUserOperation> completeAction, object userState)
        {
            this.StartOperation(new SaveUserOperation(this, this.WrapCompleteAction<SaveUserOperation>(completeAction), userState));
            return (SaveUserOperation) this.Operation;
        }

        private void StartOperation(AuthenticationOperation operation)
        {
            lock (this._syncLock)
            {
                if (this.IsBusy)
                {
                    throw new InvalidOperationException(Resources.ApplicationServices_UserServiceIsBusy);
                }
                this.Operation = operation;
            }
            try
            {
                operation.Start();
            }
            catch (Exception)
            {
                this.Operation = null;
                throw;
            }
            this.RaisePropertyChanged("IsBusy");
            this.RaisePropertyChanged(GetBusyPropertyName(this.Operation));
        }

        private Action<T> WrapCompleteAction<T>(Action<T> completeAction) where T: AuthenticationOperation
        {
            return delegate (T ao) {
                bool flag = false;
                bool flag2 = false;
                bool flag3 = false;
                if ((!ao.IsCanceled && !ao.HasError) && ((ao.User != null) && (this._user != ao.User)))
                {
                    flag2 = ((this._user == null) || (!this._user.Identity.IsAuthenticated && ao.User.Identity.IsAuthenticated)) || (ao.User.Identity.IsAuthenticated && (this._user.Identity.Name != ao.User.Identity.Name));
                    flag3 = (this._user != null) && (this._user.Identity.IsAuthenticated && !ao.User.Identity.IsAuthenticated);
                    this._user = ao.User;
                    flag = true;
                }
                this.Operation = null;
                if (completeAction != null)
                {
                    try
                    {
                        completeAction.DynamicInvoke(new object[] { ao });
                    }
                    catch (TargetInvocationException exception)
                    {
                        if (exception.InnerException != null)
                        {
                            throw exception.InnerException;
                        }
                        throw;
                    }
                }
                if (flag)
                {
                    this.RaisePropertyChanged("User");
                }
                this.RaisePropertyChanged("IsBusy");
                this.RaisePropertyChanged(GetBusyPropertyName(ao));
                if (flag2)
                {
                    this.OnLoggedIn(new AuthenticationEventArgs(ao.User));
                }
                if (flag3)
                {
                    this.OnLoggedOut(new AuthenticationEventArgs(ao.User));
                }
            };
        }

        public bool IsBusy
        {
            get
            {
                return (this.Operation != null);
            }
        }

        public bool IsLoadingUser
        {
            get
            {
                return (this.IsBusy && (this.Operation is LoadUserOperation));
            }
        }

        public bool IsLoggingIn
        {
            get
            {
                return (this.IsBusy && (this.Operation is LoginOperation));
            }
        }

        public bool IsLoggingOut
        {
            get
            {
                return (this.IsBusy && (this.Operation is LogoutOperation));
            }
        }

        public bool IsSavingUser
        {
            get
            {
                return (this.IsBusy && (this.Operation is SaveUserOperation));
            }
        }

        private AuthenticationOperation Operation { get; set; }

        protected internal virtual bool SupportsCancellation
        {
            get
            {
                return false;
            }
        }

        public IPrincipal User
        {
            get
            {
                if (this._user == null)
                {
                    this._user = this.CreateDefaultUser();
                    if (this._user == null)
                    {
                        throw new InvalidOperationException(Resources.ApplicationServices_UserIsNull);
                    }
                }
                return this._user;
            }
        }
    }
}

